#pragma once

#include <string>
#include <sstream>

class IX
{
public:
    virtual ~IX() {}
    virtual void WriteXml(std::wostream &o) const = 0;
};

class XA : public IX
{
public:
    std::wstring name_, value_;
    XA(const std::wstring &name, const std::wstring &value) :
        name_(name), value_(value) {}
    virtual ~XA() {}

    static std::wstring EncodedName(const std::wstring &n) { return n; }
    static std::wstring EncodedValue(const std::wstring &v) { return v; }

    virtual void WriteXml(std::wostream &o) const
    {
        o << EncodedName(name_) << L"=\"";
        o << EncodedValue(value_) << L"\" ";
    }
};

class XE : public IX
{
public:
    typedef std::vector<IX*> Children;
    std::wstring name_;
    Children chil_;

    XE(const std::wstring &name,
       IX *v0=0, IX *v1=0, IX *v2=0, IX *v3=0, IX *v4=0, IX *v5=0) : name_(name) {
        if (v0) chil_.push_back(v0);
        if (v1) chil_.push_back(v1);
        if (v2) chil_.push_back(v2);
        if (v3) chil_.push_back(v3);
        if (v4) chil_.push_back(v4);
        if (v5) chil_.push_back(v5);
    }

    virtual ~XE() {
        for (Children::iterator i=chil_.begin(); i != chil_.end(); ++i) {
            delete *i;
        }
    }

    virtual void WriteXml(std::wostream &o) const
    {
        bool closed = false;
        o << L"<" << XA::EncodedName(name_) << L" ";
        for (Children::const_iterator i=chil_.begin(); i != chil_.end(); ++i) {
            if (!dynamic_cast<XA*>(*i) && !closed) {
                o << L">";
                closed = true;                
            }
            (*i)->WriteXml(o);
        }
        if (closed) {
            o << L"</" << name_ << L">";
        }
        else {
            o << L"/>";
        }
    }
};

class XT : public IX
{
public:
    std::wstring text_;
    
    XT(const std::wstring &text) : text_(text) {}
    virtual ~XT() {}

    virtual void WriteXml(std::wostream &o) const
    {
        for (size_t i = 0; i < text_.size(); i++) {
            switch (text_[i]) {
                case L'<': o << L"&lt;"; break;
                case L'>': o << L"&gt;"; break;
                case L'&': o << L"&amp;"; break;
                default: o << text_[i];
            }
        }
    }
};

class XL : public IX
{
public:
    std::wstring lit_;
    
    XL(const std::wstring &lit) : lit_(lit) {}
    virtual ~XL() {}

    virtual void WriteXml(std::wostream &o) const
    {
        o << lit_;
    }
};

inline XE *xe(const wstring &name, IX *v0=0, IX *v1=0, IX *v2=0, IX *v3=0, IX *v4=0, IX *v5=0) {
    return new XE(name, v0,v1,v2,v3,v4,v5);
}

inline XA *xa(const std::wstring &name, const std::wstring &value) { return new XA(name, value); }
inline XT *xt(const std::wstring &text) { return new XT(text); }
inline XL *xl(const std::wstring &lit) { return new XL(lit); }

inline std::wstring OuterXml(const IX &x)
{
    std::wostringstream o;
    x.WriteXml(o);
    return o.str();
}
